Applying Access Modifiers to Classes, Fields, and Methods in Java
Course: Core Java Training
Topic: Applying Access Modifiers to Classes, Fields, and Methods
Course Link: Core Java Training Course
Demo Link: Register for a Free Course Demo
Website: JustAcademy Official Website
1. Introduction to Access Modifiers in Java
Access modifiers are keywords in Java that control the visibility and accessibility of classes, fields, constructors, and methods. They help developers decide which parts of a program can access specific classes and members.
In object-oriented programming, controlling access is important for protecting data, preventing unintended modifications, and designing maintainable applications. For example, a bank account should not allow external code to change its balance directly. Instead, access can be controlled through methods such as deposit() and withdraw().
Java provides four access levels: public, protected, package-private (no modifier), and private. Each level has different accessibility rules.
Learning Objectives
- Understand the purpose of access modifiers in Java.
- Differentiate between public, protected, package-private, and private access.
- Apply access modifiers to classes, fields, methods, and constructors.
- Understand how packages and inheritance affect accessibility.
- Use access modifiers to implement encapsulation and data hiding.
- Identify common access-related compilation errors.
2. What Are Access Modifiers?
An access modifier specifies where a class or class member can be accessed from. It defines the boundary between accessible and restricted parts of a Java program.
Consider a class named Student that contains a student's name, roll number, and marks. If every field is public, any accessible code could change those values directly. By using private fields and public methods, the class can control how its data is read and updated.
class Student {
private String name;
private int marks;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
In this example, the fields are private, while the methods are public. Code outside the class cannot directly access name or marks, but it can use the public methods provided by the class.
Why Are Access Modifiers Important?
- Data protection: Prevent direct access to sensitive fields.
- Encapsulation: Keep data and the operations that manage it together.
- Maintainability: Allow internal implementation details to change without exposing them unnecessarily.
- Controlled access: Provide only the operations that other classes need.
- Reusable design: Define clear interfaces between classes and packages.
3. Types of Access Modifiers in Java
Java provides four access levels. The fourth level, package-private, is the default access level when no modifier is written.
| Access Level |
Keyword |
Same Class |
Same Package |
Subclass in Another Package |
Unrelated Class in Another Package |
| Private |
private |
Yes |
No direct access |
No direct access |
No direct access |
| Package-private |
No keyword |
Yes |
Yes |
No |
No |
| Protected |
protected |
Yes |
Yes |
Yes, subject to inheritance access rules |
No |
| Public |
public |
Yes |
Yes |
Yes, subject to other language and module restrictions |
Yes, subject to other language and module restrictions |
Important: A public member is not automatically usable everywhere if its enclosing class is inaccessible, its package is not available, or Java module rules prevent access.
4. Public Access Modifier
The public modifier provides the broadest normal accessibility. A public class or member can be accessed from other packages when the declaring type is accessible and the required dependencies are available.
4.1 Applying Public to a Class
package school;
public class Student {
public String name = "Aarav";
public void display() {
System.out.println("Student Name: " + name);
}
}
The Student class can be imported and used by code in another package because it is public.
4.2 Applying Public to Fields and Methods
public class Calculator {
public int result;
public int add(int a, int b) {
result = a + b;
return result;
}
}
Other accessible classes can call add() and access result. In production code, fields are often kept private even when methods are public, so the class can control how values change.
4.3 Example of Public Access
class PublicDemo {
public void showMessage() {
System.out.println("Public method accessed");
}
}
public class Main {
public static void main(String[] args) {
PublicDemo demo = new PublicDemo();
demo.showMessage();
}
}
Output:
Public method accessed
Explanation: The showMessage() method is public, so it can be called from the Main class. Both classes are in the same package in this example.
When Should You Use Public?
- For methods intended to form part of a class's public API.
- For classes that must be accessible from other packages.
- For operations that other components are expected to call.
Avoid making internal fields public without a clear reason, because doing so exposes implementation details and makes validation harder.
5. Private Access Modifier
The private modifier restricts access to the declaring top-level class's body. Other classes cannot directly access private fields or methods, including subclasses.
Private members are commonly used to implement encapsulation and hide internal details.
5.1 Applying Private to Fields
public class Employee {
private int employeeId;
private double salary;
public Employee(int employeeId, double salary) {
this.employeeId = employeeId;
this.salary = salary;
}
public double getSalary() {
return salary;
}
}
Here, employeeId and salary are private. Code outside the class cannot directly read or assign them, but the public getter allows controlled reading of the salary.
5.2 Applying Private to Methods
public class LoginService {
public void login(String username, String password) {
if (isValid(username, password)) {
System.out.println("Login successful");
} else {
System.out.println("Invalid credentials");
}
}
private boolean isValid(String username, String password) {
return username != null
&& !username.isBlank()
&& password != null
&& !password.isBlank();
}
}
The isValid() method is private because it is an internal helper operation. External classes should use the public login() operation rather than calling the validation helper directly.
5.3 Example of Private Access
public class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public double getBalance() {
return balance;
}
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.deposit(1500);
System.out.println(account.getBalance());
}
}
Output:
1500.0
Explanation: The balance is private, so it cannot be changed directly by ordinary code outside the class. The deposit() method checks that the amount is positive before updating it.
Important Rules for Private
- A private top-level class is not allowed. A top-level class can be public or package-private.
- A nested class can be private.
- Private fields and methods are not directly accessible to subclasses.
- Private methods are not overridden by subclasses in the normal polymorphic sense.
6. Package-Private Access Modifier
Package-private access applies when no access modifier is specified. It allows access from classes in the same package but prevents direct access from classes in other packages.
Package-private access is useful when several classes within one package need to cooperate, but the functionality should not become part of the public API.
6.1 Example of Package-Private Access
package school;
class Attendance {
int totalDays = 30;
void displayAttendance() {
System.out.println("Total Days: " + totalDays);
}
}
The class Attendance, its field totalDays, and its method displayAttendance() are package-private because no access modifiers are specified.
Another class in the school package can use them:
package school;
public class AttendanceReport {
public static void main(String[] args) {
Attendance attendance = new Attendance();
attendance.displayAttendance();
}
}
Output:
Total Days: 30
What Happens in Another Package?
If a class in a different package tries to import and use school.Attendance, compilation fails because the class is not public. Even if the class were public, package-private members would remain inaccessible from outside the package.
When Should You Use Package-Private?
- For internal helper classes and methods.
- For implementation details shared among classes in one package.
- When you do not want to expose functionality as part of a public API.
7. Protected Access Modifier
The protected modifier allows access within the same package and also provides access to subclasses in other packages, subject to Java's inheritance access rules.
It is often used when a parent class provides functionality or state that subclasses are intended to use or customize.
7.1 Example Within the Same Package
package animals;
public class Animal {
protected String name = "Animal";
protected void makeSound() {
System.out.println("Animal makes a sound");
}
}
Other classes in the animals package can access the protected field and method.
7.2 Example Using Inheritance Across Packages
Suppose the parent class is in package animals, while the subclass is in package pets.
// File: animals/Animal.java
package animals;
public class Animal {
protected String name = "Dog";
protected void displayName() {
System.out.println(name);
}
}
// File: pets/Dog.java
package pets;
import animals.Animal;
public class Dog extends Animal {
public void showDetails() {
System.out.println(name);
displayName();
}
}
The Dog subclass can access the inherited protected field and method through inheritance. However, outside the declaring package, protected access is more restricted than public access: code cannot freely access a protected member through any arbitrary parent-class instance. The access must satisfy Java's subclass rules.
When Should You Use Protected?
- When subclasses need access to a member.
- When designing a base class intended for extension.
- When the member should remain hidden from unrelated classes in other packages.
Use protected carefully. Making too many fields protected can tightly couple subclasses to the internal implementation of a parent class.
8. Applying Access Modifiers to Classes
Access rules for classes depend on whether the class is declared at the top level or nested inside another class.
8.1 Top-Level Classes
A top-level class can use either public or package-private access. It cannot be declared private or protected.
public class Student {
// Accessible from other packages when the type is available.
}
class Teacher {
// Accessible only within the same package.
}
In a typical Java source file, a public top-level class must have the same name as the file. For example, Student is normally declared in Student.java.
8.2 Nested Classes
A nested class is declared inside another class. Unlike top-level classes, nested classes can use all four access levels.
public class University {
private class InternalRecord {
void display() {
System.out.println("Internal record");
}
}
public static class Department {
public void display() {
System.out.println("Department details");
}
}
}
The private nested class InternalRecord is restricted to the enclosing class's access scope, while the public static nested class Department can be accessed through the public enclosing type, provided the type is otherwise accessible.
9. Applying Access Modifiers to Fields
Fields store an object's or class's data. Their access level determines which code can directly read or change that data.
| Modifier |
Typical Use |
Example |
private |
Internal data protected by methods |
private double balance; |
| No modifier |
Data shared inside one package |
int count; |
protected |
Data intended for package access and subclasses |
protected String name; |
public |
Data intentionally exposed to callers |
public static final int MAX_SIZE = 100; |
Recommended Approach
Fields are usually declared private, while controlled access is provided through public methods when required.
public class Product {
private String productName;
private double price;
public Product(String productName, double price) {
this.productName = productName;
setPrice(price);
}
public String getProductName() {
return productName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
if (price >= 0) {
this.price = price;
}
}
}
The setter validates the price before updating it. This approach prevents ordinary callers from assigning a negative price directly to the private field.
10. Applying Access Modifiers to Methods
Methods define the operations that an object can perform. Their access modifiers determine who can call them.
public class ReportService {
public void generateReport() {
loadData();
formatReport();
System.out.println("Report generated");
}
private void loadData() {
System.out.println("Data loaded");
}
private void formatReport() {
System.out.println("Report formatted");
}
}
The public method generateReport() is the operation exposed to callers. The private methods perform internal steps and can be changed without requiring callers to change their code.
Output when generateReport() is called:
Data loaded
Report formatted
Report generated
Choosing a Method's Access Level
- Use
public for operations intended for external callers.
- Use
private for internal helper operations.
- Use package-private for operations shared only within a package.
- Use
protected when subclasses need access as part of an inheritance design.
11. Applying Access Modifiers to Constructors
Constructors initialize new objects. Their access modifiers control where an object can be created directly.
11.1 Public Constructor
public class Customer {
public Customer() {
System.out.println("Customer created");
}
}
A public constructor allows accessible code to instantiate the class from other packages.
11.2 Private Constructor
public class AppConfig {
private AppConfig() {
}
public static void showConfig() {
System.out.println("Application configuration");
}
}
The private constructor prevents ordinary code outside the class from directly creating an AppConfig object. This pattern can be useful for utility classes or designs that control instance creation.
11.3 Protected and Package-Private Constructors
A protected constructor permits access from the same package and, under Java's protected-constructor rules, from appropriate subclasses outside the package. A package-private constructor can be called by code in the same package but not by unrelated code in another package.
12. Access Modifiers and Encapsulation
Encapsulation is the practice of combining data and behavior in a class while controlling access to internal state. Access modifiers support encapsulation by limiting which fields and methods can be used directly.
public class BankAccount {
private String accountHolder;
private double balance;
public BankAccount(String accountHolder) {
this.accountHolder = accountHolder;
this.balance = 0;
}
public String getAccountHolder() {
return accountHolder;
}
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
}
Example usage:
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount("Aarav");
account.deposit(2000);
account.withdraw(500);
System.out.println(account.getAccountHolder());
System.out.println(account.getBalance());
}
}
Output:
Aarav
1500.0
The private balance cannot be modified directly by ordinary external code. The public methods define how deposits and withdrawals are performed, and the withdrawal method prevents withdrawing more than the available balance.
13. Access Modifiers and Inheritance
Inheritance allows a subclass to extend a superclass. Access modifiers determine which superclass members the subclass can use.
| Member Type |
Subclass in Same Package |
Subclass in Different Package |
| Public |
Accessible |
Accessible when the declaring type is accessible |
| Protected |
Accessible |
Accessible through the subclass inheritance rules |
| Package-private |
Accessible |
Not directly accessible |
| Private |
Not directly accessible |
Not directly accessible |
Private members still exist as part of the superclass's implementation and can be used by its own methods, but a subclass cannot directly reference them by name. A superclass may expose controlled access through public or protected methods.
Example
class Vehicle {
private int speed = 60;
protected String brand = "Toyota";
public void displaySpeed() {
System.out.println("Speed: " + speed);
}
}
class Car extends Vehicle {
public void displayCar() {
System.out.println("Brand: " + brand);
displaySpeed();
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.displayCar();
}
}
Output:
Brand: Toyota
Speed: 60
Explanation: The subclass can access the protected field brand. It cannot directly read the private field speed, but it can call the public method displaySpeed(), which accesses that field inside the declaring class.
14. Access Modifiers and Method Overriding
When a subclass overrides an inherited instance method, it cannot reduce the visibility of that method. The overriding method must have the same or broader access.
class Parent {
protected void display() {
System.out.println("Parent display");
}
}
class Child extends Parent {
@Override
public void display() {
System.out.println("Child display");
}
}
This is valid because public is more accessible than protected.
The following example is invalid:
class Parent {
public void display() {
System.out.println("Parent display");
}
}
class Child extends Parent {
// Compilation error: weaker access privileges
protected void display() {
System.out.println("Child display");
}
}
Key rule: An overriding method may widen visibility, but it cannot narrow the visibility of the inherited method. Private methods are not overridden in the usual sense because they are not inherited as accessible methods by subclasses.
15. Practical Example: Employee Management System
This example demonstrates access modifiers in a small employee management application.
public class Employee {
private int id;
private String name;
private double salary;
public Employee(int id, String name, double salary) {
this.id = id;
this.name = name;
setSalary(salary);
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
if (salary >= 0) {
this.salary = salary;
}
}
protected void displayInternalDetails() {
System.out.println("Employee ID: " + id);
System.out.println("Employee Name: " + name);
}
public void displayDetails() {
displayInternalDetails();
System.out.println("Salary: " + salary);
}
}
Test class:
public class Main {
public static void main(String[] args) {
Employee employee = new Employee(101, "Riya", 45000);
employee.displayDetails();
employee.setSalary(50000);
System.out.println("Updated Salary: " + employee.getSalary());
}
}
Output:
Employee ID: 101
Employee Name: Riya
Salary: 45000.0
Updated Salary: 50000.0
Concepts demonstrated:
- Private fields protect the employee's internal data.
- Public methods provide controlled access to employee details.
- The salary setter validates the value before updating it.
- The protected method can support access from subclasses and same-package code.
- The public display method provides a simple operation for callers.
16. Common Mistakes When Using Access Modifiers
Mistake 1: Accessing a Private Field Directly
class Student {
private int marks = 90;
}
class Main {
public static void main(String[] args) {
Student student = new Student();
// Compilation error:
// System.out.println(student.marks);
}
}
Solution: Add an appropriate public getter if outside code needs to read the value.
Mistake 2: Declaring a Top-Level Class Private
// Invalid:
private class Student {
}
Solution: Declare the top-level class as public or use package-private access by omitting the modifier.
Mistake 3: Assuming Protected Means Public
A protected member is not accessible to every unrelated class in every package. Cross-package access must satisfy the subclass rules.
Solution: Use public only when broad access is intended. Otherwise, keep protected members within a deliberate inheritance design.
Mistake 4: Reducing Visibility When Overriding
A public superclass method cannot be overridden with a protected or private method.
Solution: Preserve or widen the original method's visibility.
Mistake 5: Making Every Field Public
Public fields let other classes change internal data without validation. This can lead to inconsistent object state.
Solution: Prefer private fields and expose necessary operations through methods.
Mistake 6: Confusing Default Access with Public Access
Omitting an access modifier does not make a class or member public. Package-private members are restricted to the declaring package.
Solution: Explicitly declare public members when they are intended for use outside the package.
17. Best Practices for Using Access Modifiers
- Follow least privilege: Give each class or member only the visibility it needs.
- Keep fields private: Prefer controlled methods over unrestricted direct field access.
- Expose a clear public API: Keep public methods focused on the operations callers need.
- Use package-private for internal collaboration: Avoid exposing implementation details across packages unnecessarily.
- Use protected deliberately: Expose members to subclasses only when inheritance is part of the intended design.
- Validate changes: Use methods to enforce business rules before changing important data.
- Review nested types: Choose their access levels based on whether they are internal helpers or reusable types.
- Understand inheritance: Check visibility rules before accessing superclass members or overriding methods.
- Avoid unnecessary getters and setters: Provide methods that reflect the class's intended behavior rather than automatically exposing every field.
- Document public APIs: Use clear names and documentation for methods intended for other developers.
18. Hands-On Exercises
Exercise 1: Student Class
- Create a
Student class with private fields for name, roll number, and marks.
- Add public methods to read the values.
- Add a method to update marks only when the value is between 0 and 100.
- Test valid and invalid marks.
Exercise 2: Package-Private Access
- Create two classes in the same package.
- Declare one field and one method without access modifiers.
- Access them from the second class.
- Move the second class to another package and observe the compilation errors.
Exercise 3: Protected Inheritance
- Create a superclass with a protected field and method.
- Create a subclass in the same package and access those members.
- Move the subclass to a different package and test access through inheritance.
Exercise 4: Employee Salary
- Declare salary as private.
- Provide a public method to update the salary after validation.
- Try to assign salary directly from another class and observe the compilation error.
- Explain why private fields improve encapsulation.
Mini Project: Library Management System
Create a basic library application using access modifiers correctly.
- Create a public
Book class.
- Declare the book ID, title, and availability status as private fields.
- Provide public methods to view book details and borrow or return a book.
- Use private helper methods for internal validation.
- Use package-private helpers for operations shared only within the library package.
- Test that invalid operations do not change the book's state.
19. Interview Questions and Answers
Q1. What are access modifiers in Java?
Answer: Access modifiers control the visibility of classes and members. Java provides public, protected, private, and package-private access.
Q2. How many access modifiers does Java provide?
Answer: Java has three explicit access-modifier keywords: public, protected, and private. Including package-private, which is represented by no keyword, there are four access levels.
Q3. What is the difference between public and private?
Answer: Public members can be accessed from any location where the declaring type and other access requirements permit. Private members are directly accessible only within the declaring top-level class's body.
Q4. What is package-private access?
Answer: Package-private access applies when no access modifier is specified. The class or member is accessible from code in the same package.
Q5. What is the purpose of protected?
Answer: Protected access allows use within the same package and by appropriate subclasses in other packages, subject to Java's inheritance access rules.
Q6. Can a top-level class be private?
Answer: No. A top-level class can be public or package-private. Nested classes can use private and protected access as well.
Q7. Why are fields usually private?
Answer: Private fields protect internal state from uncontrolled changes. Public methods can validate input and provide controlled access.
Q8. Can a private method be overridden?
Answer: No, not in the normal overriding sense. A private method is not inherited as an accessible method by subclasses. A subclass may declare a separate method with the same signature, but that does not override the private method.
Q9. Can an overriding method have broader access?
Answer: Yes. An overriding method may widen visibility, such as changing protected access to public, but it cannot reduce visibility.
Q10. Can constructors have access modifiers?
Answer: Yes. Constructors can be public, protected, private, or package-private. Their access levels control where instances can be created directly.
Q11. Can a public method access private fields of its own class?
Answer: Yes. Methods declared within the same class can access that class's private fields and methods.
Q12. What is the relationship between access modifiers and encapsulation?
Answer: Access modifiers support encapsulation by restricting direct access to internal state and exposing only the operations that callers need.
20. Quick Revision Notes
public: broadest normal access, subject to type, package, and module accessibility.
private: direct access restricted to the declaring top-level class's body.
- Package-private: no modifier; accessible within the same package.
protected: same-package access plus qualified subclass access across packages.
- Top-level classes can be public or package-private, not private or protected.
- Nested classes can use all four access levels.
- Private fields help protect object state.
- Public methods can provide controlled operations and validation.
- Protected members should be designed carefully when inheritance is intended.
- An overriding method cannot reduce the visibility of the method it overrides.
- Constructor visibility determines where a class can be instantiated directly.
- Prefer the least permissive access level that satisfies the design.
21. Conclusion
Applying access modifiers correctly is an essential part of writing secure, maintainable, and reusable Java programs. Public access supports operations intended for external callers, private access protects implementation details, package-private access enables collaboration within a package, and protected access supports package-level use and inheritance.
By choosing appropriate access levels for classes, fields, methods, and constructors, developers can implement encapsulation, reduce unwanted dependencies, and create clearer APIs. Practice these rules with small programs and multiple-package examples to build a strong foundation in Core Java and object-oriented programming.
Explore more learning opportunities through the JustAcademy Core Java Training Course, register for a course demo, or visit the JustAcademy website.